// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.logic.chat_root.sendvoice;

import android.app.Activity;
import android.content.Context;
import android.util.Log;

import com.eva.android.widget.WidgetUtils;
import com.eva.android.widget.WidgetUtils.ToastType;
import com.eva.epc.common.file.FileHelper;
import com.x52im.rbchat.Const;
import com.x52im.rbchat.R;
import com.x52im.rbchat.logic.chat_friend.utils.ChatDataHelper;
import com.x52im.rbchat.logic.chat_friend.utils.MessageHelper;
import com.x52im.rbchat.logic.chat_group.utils.GMessageHelper;
import com.x52im.rbchat.logic.chat_guest.utils.TMessageHelper;
import com.x52im.rbchat.logic.chat_root.model.Message;
import com.x52im.rbchat.logic.chat_root.model.MessageExt;
import com.x52im.rbchat.utils.PromtHelper;
import com.x52im.rbchat.logic.chat_guest.utils.TChatDataHelper;
import com.x52im.rbchat.logic.chat_group.utils.GChatDataHelper;
import com.x52im.rbchat.logic.chat_root.sendimg.PreviewAndSendActivity;

import net.x52im.mobileimsdk.server.protocal.Protocal;

import java.io.File;
import java.util.Observable;
import java.util.Observer;

/**
 * 语音留言消息发送的真正实现类.
 * <p>
 * 此类通知在类{@link SendVoiceWrapper}中使用.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 * @since 2.3
 */
public class SendVoiceProcessor
{
	private final static String TAG = SendVoiceProcessor.class.getSimpleName();
	
	private Context context = null;
	
	/** 将要被发送的语音文件临时路径（本类要发送的就是它） */
	private String tempVoicePath = null;
	/** 在群组聊天中，此参数存放的是群的id，否则一对一聊天时存放的是聊天对象的uid */
	private String friendUid = null;
	/** 功能应用场景:如用于陌生人聊天、一对一好友聊、世界频道聊等. 
	 * @see {@link PreviewAndSendActivity} */
	private String usedFor = "";
	/** 语音时长(用于文件命名时)（单位：毫秒） */
	private long duration = 0;
	
	/** 
	 * 经过重命名处理后真正要发送出去的文件名（此文件名可作为发送方的本地缓存直接使用）.
	 * 除非重命名处理成功完成，否则本字段将一直是null. */
	private String processedFileName = null;
	
	public SendVoiceProcessor(Context context
			, String usedFor
			, String friendUid
			, String tempVoicePath
			, long duration)
	{
		this.context = context;
		this.usedFor = usedFor;
		this.friendUid = friendUid;
		this.tempVoicePath = tempVoicePath;
		this.duration = duration;
	}
	
	/**
	 * 真正的文件发送过程.
	 */
	public void doSend()
	{
		boolean beforeSendOK = beforeSend(context
				, tempVoicePath
				, duration
				, SendVoiceHelper.MIN_DURATION_FOR_SEND
				, new Observer(){
					@Override
					public void update(Observable observable, Object data)
					{
						// 将重命名后的文件名保存到全局变量保存
						processedFileName = (String)data;
					}
				}
		);
		if(beforeSendOK)
		{
		    // 开始真正的发送处理环节
            SendVoiceProcessor.doSendLogic(context, friendUid, usedFor, processedFileName);

//			// 开始发送语音的提示音
//			PromtHelper.voiceSendPromt(context);
//
//			// 先生成指纹码
//			String fingerPring = Protocal.genFingerPrint();
//
//			if(PreviewAndSendActivity.USED_FOR_LOVER$CHAT.equals(usedFor))// 用于正式聊天的语音留言发送
//				ChatDataHelper.addMsgItemToChat_TO_VOICE(context, friendUid, processedFileName, fingerPring);
//			else if(PreviewAndSendActivity.USED_FOR_TEMP$CHAT.equals(usedFor))// 用于临时聊天时的留言发送
//				TChatDataHelper.addMsgItemToChat_TO_VOICE(context, friendUid, processedFileName, fingerPring);
//			else if(PreviewAndSendActivity.USED_FOR_BBS$CHAT.equals(usedFor))// 用于临时聊天时的留言发送)
//				GChatDataHelper.addMsgItemToChat_TO_VOICE(context, friendUid, processedFileName, fingerPring);
		}
	}

	/**
	 * 开始真正的语音留言发送完整逻辑（加入一行数据到聊天列表模型中、并执行语音上传、指令发送等逻辑）。
	 *
	 * @param context
	 * @param to 要发送的对象（如果是一对一聊天则本参数是对方的uid，如果是世界频道或群聊则本参数为gid）
	 * @param usedFor 用途
	 * @param processedFileName 经过前置处理后的最终文件名（前置处理包括：计算md5码、重命名等环节）
	 */
	public static void doSendLogic(Context context, String to, String usedFor, String processedFileName)
	{
		doSendLogic(context, false, to, null, usedFor, processedFileName, null);
	}

    /**
     * 开始真正的语音留言发送完整逻辑（加入一行数据到聊天列表模型中、并执行语音上传、指令发送等逻辑）。
     *
     * @param context
     * @param to 要发送的对象（如果是一对一聊天则本参数是对方的uid，如果是世界频道或群聊则本参数为gid）
	 * @param toName 要发送的对象名（当前仅用于陌生人聊天时）
     * @param usedFor 用途
     * @param processedFileName 经过前置处理后的最终文件名（前置处理包括：计算md5码、重命名等环节）
	 * @param sucessObsExtra 消息发送完成后，额外要做的事
     */
	public static void doSendLogic(Context context, boolean forForward, String to, String toName, String usedFor, String processedFileName, Observer sucessObsExtra)
    {
        // 开始发送语音的提示音
        PromtHelper.voiceSendPromt(context);

        // 先生成指纹码
        String fingerPring = Protocal.genFingerPrint();

		Message m = MessageExt.createChatMsgEntity_OUTGO_VOICE(processedFileName, fingerPring);

		// 如果是用于消息转发，需要直接跳过文件的处理过程（因为当转发的是收到的文件时，很可能这个文件还没下载过，
		// 而且转发的逻辑，本来就是只转发成功被发送的消息，所以不走文件处理流程完全合理）
		if(forForward) {
			m.setSendStatusSecondary(Message.SendStatusSecondary.none);
		}

		// 用于正式聊天的语音留言发送
        if(Const.CHAT_TYPE_FREIDN$CHAT.equals(usedFor)) {
			ChatDataHelper.addChatMessageData_outgoing(context, to, m);

			if(forForward) {
				// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
				MessageHelper.sendVoiceMessageAsync((Activity) context, to, processedFileName, fingerPring, null);
			}
		}
		// 用于临时聊天时的留言发送
		else if(Const.CHAT_TYPE_GUEST$CHAT.equals(usedFor)) {
			TChatDataHelper.addChatMessageData_outgoing(context, to, m);

			if(forForward) {
				// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
				TMessageHelper.sendVoiceMessageAsync((Activity) context, to, toName, processedFileName, fingerPring, null);
			}
		}
		// 用于群组聊天时的留言发送)
		else if(Const.CHAT_TYPE_GROUP$CHAT.equals(usedFor)) {
			GChatDataHelper.addChatMessageData_outgoing(context, to, m);

			if(forForward) {
				// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
				GMessageHelper.sendVoiceMessageAsync((Activity) context, to, processedFileName, fingerPring, null);
			}
		}

		// 由于历史原因，rbp中图片和语音留言消息的特殊发送逻辑，无法做到在真正的send方法发送完成后进行调用，暂时就先放到这里执行，不太完美但也够用了
		if(sucessObsExtra != null) {
			sucessObsExtra.update(null, m);
		}
	}
	
	/**
	 * 发送前的处理过程.包括录音最少时长检查、重命名.
	 * 
	 * @return
	 */
	public static boolean beforeSend(Context context ,String tempVoicePath
			, long duration, long MIN_DURATION_FOR_SEND
			, Observer notifyObserverAfterSucess)
	{
		boolean sucess = false;
		
		try
		{
			// 文件是否存的检查
			if(tempVoicePath == null || !new File(tempVoicePath).exists())
			{
				Log.w(TAG, "【SendVoice】要发送的声音文件"+tempVoicePath+"不存在，本地发送没有继续！");
				WidgetUtils.showToast(context, context.getString(R.string.chat_sendvoice_voice_file_not_exists), ToastType.INFO);
				return false;
			}
			
			// 允许发送的最小时长的检查
			if((duration / 1000) < MIN_DURATION_FOR_SEND)//SendVoiceHelper.MIN_DURATION_FOR_SEND)
			{
				Log.w(TAG, "【SendVoice】要发送的声音文件"+tempVoicePath+"时长太短duration="
						+duration+"毫秒(MIN="+SendVoiceHelper.MIN_DURATION_FOR_SEND+"秒)，本地发送没有继续！");
				WidgetUtils.showToast(context, context.getString(R.string.chat_sendvoice_voice_is_small), ToastType.INFO);
				return false;
			}

			// 使用文件的MD5码重命名文件
			boolean renameOk = renameUseMD5(new File(tempVoicePath), duration, notifyObserverAfterSucess);
			if(renameOk)
			{
				Log.d(TAG, "【SendVoice】要发送的声音文件重命名完成.");
				// 
				sucess = true;
			}
			else
			{
				Log.e(TAG, "【SendVoice】要发送的声音文件重命名失败！");
				WidgetUtils.showToast(context, context.getString(R.string.chat_sendvoice_voice_rename_faild), ToastType.INFO);
			}
		}
		catch (Exception e)
		{
			Log.e(TAG, "【SendVoice】声音文件发送前处理的过程中出错了！", e);
			WidgetUtils.showToast(context, context.getString(R.string.chat_sendvoice_voice_send_faild), ToastType.INFO);
		}
		
		return sucess;
	}
	
	/**
	 * 将临时声音文件重命名.
	 * 
	 * @param tempFileSavedPath
	 * @return
	 */
	public static boolean renameUseMD5(File tempFileSavedPath, long durationOfVoice
			, Observer notifyObserverAfterSucess)
	{
		boolean sucess = false;
		// 计算出头像文件的MD5码
		byte[] fileData = null;
		try
		{
			// 将要上传的声音文件数据读取出来
			fileData = FileHelper.readFileWithBytes(tempFileSavedPath);

			// 读取出来的数据是空的，上传当然就没有必要继续了
			if(fileData == null)
				return false;

			// 计算声音文件的MD5码
			final String fileMd5 = getTempFileMD5(fileData);
			if(fileMd5 != null)
			{
				// 本户存放于数据库、本地缓存的文件名格式是："时长_MD5码.amr",形如:"120000_0b272fca28252641231a94f63d8e25fa.amr"
				String fileNameUsedMd5 = SendVoiceHelper.constructVoiceFileName(durationOfVoice, fileMd5);
				
				File fileAfterRename = null;
				try
				{
					//
					fileAfterRename = new File(tempFileSavedPath.getParent()+"/"+fileNameUsedMd5);
					tempFileSavedPath.renameTo(fileAfterRename);
					
					// 通知观察者
					if(notifyObserverAfterSucess != null)
						notifyObserverAfterSucess.update(null, fileAfterRename.getName()); // 重命名后的文件名作为data返回
//					//
//					processedFileName = fileAfterRename.getName();
					sucess = true;
				}
				catch (Exception e)
				{
					Log.e(TAG, "【SendVoice】将临时文件"+tempFileSavedPath+"重命名失败了，上传将不能继续！", e);
					// 如果重命名失败，就尝试把这个文件删除（没有必要留了）
					try{
						fileAfterRename.delete();
					}
					catch (Exception e2){
					}
				}
			}
		}
		// 显式处理下OOM使得APP更健壮（OOM只能显示抓取，否则会按系统Error的方式报出从而使APP崩溃哦）
		catch (OutOfMemoryError e)
		{
			Log.e(TAG, "【SendVoice】将声音文件数据读取到内存时内存溢出了，上传没有继续！", e);
		}
		catch (Exception e)
		{
			Log.e(TAG, "【SendVoice】尝试将声音临时文件数据读取出来时出错了，"+e.getMessage()+"，上传将不能继续！", e);
		}

		return sucess;
	}
	
	private static String getTempFileMD5(byte[] fileData)
	{
		try
		{
			return FileHelper.getFileMD5(fileData);
		}
		catch (Exception e)
		{
			Log.w(TAG, "【SendVoice】计算MD5码时出错了，"+e.getMessage(), e);
			return null;
		}
	}
}